#include "XmlNode.h"
#include <libxml/tree.h>
#include <string.h>

#pragma comment(lib, "libxml2.lib")

namespace tinkle{


/////////////////////////////////////////////////////////////////
	class XmlElementImp_1 : public XmlElement {
	public:
		XmlElementImp_1(xmlNodePtr node);
		~XmlElementImp_1();

		virtual bool IsNull() const {return false;}

		virtual std::string GetName() const;
		virtual void SetName(const std::string& name);

		virtual void DeclNameSpace(const std::string &href, const std::string &prefix);
		virtual void SetNameSpace(const std::string &prefix);
		//get the c pointer
		
		virtual std::string GetNsDecl(const std::string& prefix) const;
		virtual std::string GetPrefix() const;

		virtual void SetAttrValue(const std::string &name, const std::string &value, const std::string& ns = "");
		virtual std::string GetAttrValue(const std::string &name,  const std::string &ns = "");

		virtual ElementPtr AddChild(const std::string& name);
		virtual ElementPtr GetChild(const std::string &name);

		virtual void SetContent(const std::string& name);
		virtual std::string GetContent() const;
		
	protected:
		xmlNodePtr _find_child(const xmlChar* c) const;
		xmlNsPtr _xmlSearchNs(const xmlChar* ns) const;
		xmlNodePtr _p;
	};

/////////////////////////////////////////////////////////////////
	class XmlElementImp_NULL : public XmlElement {
	public:
		XmlElementImp_NULL() {};

		virtual bool IsNull() const {return true;}

		virtual std::string GetName() const {return "";}
		virtual void SetName(const std::string& name) {};
		
		virtual void DeclNameSpace(const std::string &href, const std::string &prefix) {}; 
		virtual void SetNameSpace(const std::string &ns) {};
		virtual std::string GetNsDecl(const std::string& prefix) const {return "";}
		virtual std::string GetPrefix() const {return "";}
		virtual void SetAttrValue(const std::string &name, const std::string &value, const std::string& ns = "") {}
		virtual std::string GetAttrValue(const std::string &name, const std::string &ns /* = */ ) {
			return "";
		}
		virtual ElementPtr AddChild(const std::string& name) {return ElementPtr(new XmlElementImp_NULL());}
		virtual ElementPtr GetChild(const std::string &name) {return ElementPtr(new XmlElementImp_NULL());}

		virtual std::string GetContent() const {return "";}
		virtual void SetContent(const std::string& content) {}

	};
//////////////////////////////////////////////////////////////////	

	XmlNode::~XmlNode(){
		xmlFreeNode(_p);
	}
	
	XmlNode::XmlNode(const std::string& name) {
		_p = xmlNewNode(NULL, BAD_CAST name.c_str());
	}
	
	XmlNode::XmlNode(const XmlNode& another) {
		_p = NULL;
		*this = another;
	}
	XmlNode& XmlNode::operator =(const XmlNode& another) {
		if (_p) xmlFreeNode(_p);
		_p = xmlCopyNode(another._p, 1); // do recursive copy
		return *this;
	}

	ElementPtr XmlNode::GetElement() const {
		
		return ElementPtr(new XmlElementImp_1(_p));		
	}

	std::string XmlNode::Dump() const {
		xmlBufferPtr buf = xmlBufferCreate();
		xmlNodeDump(buf, NULL, _p, 0, 0);
		std::string ret(reinterpret_cast<const char*>(buf->content));
		xmlBufferFree( buf );
		return ret;
	}

	ElementPtr XmlNode::operator () () const {
		return GetElement();
	}
/////////////////////////////////////////////////////////////////

	xmlNsPtr XmlElementImp_1::_xmlSearchNs(const xmlChar* ns) const {
		xmlNsPtr ret = NULL;
		if ( (ret = xmlSearchNs(NULL, _p, ns)) != NULL)
			return ret;

		ret = _p->nsDef;
		while (ret != NULL) {
			if (ret->prefix)
            {
			    if ( 0 == xmlStrcmp(ns, ret->prefix) ) 
				    break;
            }
            else {
                if (ns[0] == '\0')
                    return ret;
            }
			ret = ret->next;
		}

		return  ret;			
	}

	XmlElementImp_1::XmlElementImp_1(xmlNodePtr node) {
		_p = node;
	}
	XmlElementImp_1::~XmlElementImp_1() {}

	xmlNodePtr XmlElementImp_1::_find_child(const xmlChar* c) const {
		xmlNodePtr ret = _p->children;
		while (ret) {
			if (ret->name)
				if (0 == xmlStrcmp(c, ret->name))
					break;				
			ret = ret->next;
		}

		return ret;
	}


	std::string XmlElementImp_1::GetName() const{
		return std::string(reinterpret_cast<const char*>(_p->name));
	}
	

	void XmlElementImp_1::SetName(const std::string& name) {
		xmlNodeSetName(_p, BAD_CAST name.c_str());
	}


	void XmlElementImp_1::DeclNameSpace(const std::string &href, const std::string &prefix) {
		if (prefix.length() > 0)			
			xmlNewNs(_p, BAD_CAST href.c_str(), BAD_CAST prefix.c_str());
		else xmlNewNs(_p, BAD_CAST href.c_str(), NULL );
	}


	void XmlElementImp_1::SetNameSpace(const std::string &prefix) {
		
		//if prefix == "", means that cancel namespace setting on this element		
		if (prefix.length() == 0){
			xmlNsPtr ns = xmlNewNs(_p, NULL, BAD_CAST "");
			xmlSetNs(_p, NULL);
			return;
		}
	
		//xmlNsPtr ns = xmlSearchNs(NULL, _p, BAD_CAST prefix.c_str());	
		xmlNsPtr ns = _xmlSearchNs(BAD_CAST prefix.c_str());	

		if ( NULL == ns ) {
			//the namespace didn't exsit, create one
			ns = xmlNewNs(_p, NULL, BAD_CAST prefix.c_str());
			xmlSetNs(_p, ns);
			return; 
		}

		xmlSetNs(_p, ns);
	}



	std::string XmlElementImp_1::GetNsDecl(const std::string& prefix)  const {
		xmlNsPtr ns = _xmlSearchNs(BAD_CAST prefix.c_str());				
		return (ns == NULL) ?  "" : std::string(reinterpret_cast<const char*>(ns->href));
	}

	std::string XmlElementImp_1::GetPrefix() const {
		//if it associated with a name space , return it 
		
		return (_p->ns && _p->ns->prefix) ? std::string(reinterpret_cast<const char*>(_p->ns->prefix)) : "";
	}

	void XmlElementImp_1::SetAttrValue(const std::string &name, const std::string &value, const std::string& ns){
		//non meanings
		if (name.length() == 0 || value.length() == 0) return;

		if (ns.length() == 0) {
			xmlSetProp(_p, BAD_CAST name.c_str(), BAD_CAST value.c_str());
			return;
		}

/*
		xmlNsPtr nsptr = _xmlSearchNs(BAD_CAST ns.c_str());		

		//todo improve this part
		
		
		if (NULL == nsptr) {
			nsptr = xmlNewNs(_p, NULL, BAD_CAST ns.c_str());
			//xmlNewNsProp(_p, nsptr, BAD_CAST name.c_str(), BAD_CAST value.c_str());
			
		}

		xmlSetNsProp(_p, nsptr, BAD_CAST name.c_str(), BAD_CAST value.c_str());
*/
	}
	
	std::string XmlElementImp_1::GetAttrValue(const std::string &name, const std::string &ns /* = */ ) {

		if (ns.length() == 0) {
			xmlChar* sz = xmlGetProp(_p, BAD_CAST name.c_str());			
			if (sz) {
				std::string ret(reinterpret_cast<char*>(sz));
				xmlFree(sz);
				return ret;
			}			
		}
		
		return "";
	}


	ElementPtr XmlElementImp_1::AddChild(const std::string& name){
		xmlNodePtr child = xmlNewChild(_p, NULL, BAD_CAST name.c_str(), NULL);
		return ( child != NULL ) ? ElementPtr(new XmlElementImp_1(child)) : ElementPtr(new XmlElementImp_NULL());
	}
	
	ElementPtr XmlElementImp_1::GetChild(const std::string &name) {
		xmlNodePtr child = _find_child(BAD_CAST name.c_str());
		return ( child != NULL ) ? ElementPtr(new XmlElementImp_1(child)) : ElementPtr(new XmlElementImp_NULL());
	}
	
	void XmlElementImp_1::SetContent(const std::string &name) {
		xmlNodeSetContent(_p, BAD_CAST name.c_str());
	}

	std::string XmlElementImp_1::GetContent() const {
		xmlChar* sz = xmlNodeGetContent(_p);
		return (NULL == sz) ? "" : std::string(reinterpret_cast<char*>( sz ));
	}
	
}